package com.a0soft.gphone.aTruffleHog.di;
/*
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 3, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * Copyright(C) 2011 
 *   Sam Lu <ysl.sam.lu (at) gmail.com>
 *
 */

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.List;
import java.util.Locale;
import java.util.UUID;

import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;

import com.a0soft.gphone.aTruffleHog.util.MiscUtil;
import com.a0soft.gphone.aTruffleHog.util.aDebug;
import com.a0soft.gphone.aTruffleHog.util.aSystemProperties;

class UserInfo extends BaseInfo {    
	private static final String LOG_TAG = UserInfo.class.getSimpleName();
	
	static void
    Build(DeviceInfo di)
    {
		Locale loc = Locale.getDefault();
		if (aDebug.ENABLED) {
			aDebug.d(LOG_TAG, "Country=" + loc.getCountry());
			aDebug.d(LOG_TAG, "Language=" + loc.getLanguage());
			aDebug.d(LOG_TAG, "ISO3Country=" + loc.getISO3Country());
			aDebug.d(LOG_TAG, "ISO3Language=" + loc.getISO3Language());
			aDebug.d(LOG_TAG, "Variant=" + loc.getVariant());
			aDebug.d(LOG_TAG, "DisplayCountry=" + loc.getDisplayCountry());
			aDebug.d(LOG_TAG, "DisplayLanguage=" + loc.getDisplayLanguage());
			aDebug.d(LOG_TAG, "DisplayName=" + loc.getDisplayName());
			aDebug.d(LOG_TAG, "DisplayVariant=" + loc.getDisplayVariant());
		}
		
		di.Put(DeviceInfoKey.K.UR_LANG, loc.getLanguage().toLowerCase());
		di.Put(DeviceInfoKey.K.UR_LANG_REGION, loc.getCountry().toLowerCase());
		
		di.Put(DeviceInfoKey.K.UR_COUNTRY, GetCountryCode());
		di.Put(DeviceInfoKey.K.UR_ID, GetUserID());
    }
	
	public static String
	GetCountryCode()
	{
		Context ctx = MiscUtil.GetAppContext();
		String strCode;
		
		strCode = _GetCountryCodeFromSimCard(ctx);
		if (!TextUtils.isEmpty(strCode)) return strCode;

		strCode = _GetCountryCodeFromLocation(ctx);
		if (!TextUtils.isEmpty(strCode)) return strCode;
		
		Locale loc = Locale.getDefault();
		strCode = loc.getCountry().toLowerCase();
		if (!TextUtils.isEmpty(strCode)) return strCode;
		
		return Locale.US.getCountry().toLowerCase();
	}
	
	private static String
	_GetCountryCodeFromLocation(Context ctx)
	{		
		long lLatestTime = Long.MIN_VALUE;
		Location locLatest = null;		
		LocationManager lm = (LocationManager)ctx.getSystemService(Context.LOCATION_SERVICE);
		List<String> strProviders = lm.getProviders(false);
		for (String strProvider: strProviders) {
		    if (aDebug.ENABLED) {
                aDebug.d(LOG_TAG, "strProvider=" + strProvider);
            }
		    
		    //add try/catch as the ACCESS_xxx_LOCATION may not be specified
		    try { 
    			Location loc = lm.getLastKnownLocation(strProvider);
    			if (loc != null) {
    			    if (aDebug.ENABLED) {
    	                aDebug.d(LOG_TAG, "  loc=" + loc.getLatitude() + "," + loc.getLongitude());
    	            }
    				if (lLatestTime < loc.getTime()) {
    					lLatestTime = loc.getTime();
    					locLatest = loc;
    				}
    			}
		    } catch (Exception e) {
		        if (aDebug.ENABLED)
		            aDebug.d(LOG_TAG, "_GetCountryCodeFromLocation(): " + strProvider, e);
		    }
		}
		
		if (locLatest != null) {		    
			try {
				if (aDebug.ENABLED) {
					aDebug.d(LOG_TAG, "Latitude=" + locLatest.getLatitude());
					aDebug.d(LOG_TAG, "Longitude=" + locLatest.getLongitude());
				}
				/*
				 * 1. if Geocoder is not available, call
				 *    http://ws.geonames.org/findNearbyPlaceNameJSON?lat=25&lng=121 or
				 *    http://api.geonames.org/findNearbyJSON?formatted=true&lat=25&lng=121&username=demo
				 */
				Geocoder gc = new Geocoder(ctx);
				List<Address> addrs = gc.getFromLocation(locLatest.getLatitude(), locLatest.getLongitude(), 1);
				if (addrs != null && addrs.size() > 0) {
					Address addr = addrs.get(0);
					if (aDebug.ENABLED) {
						for (int i = 0; i < addr.getMaxAddressLineIndex(); i++) {
							aDebug.d(LOG_TAG, "Address " + (i + 1) + "=" + addr.getAddressLine(i));
						}
					}
					
					String strCntyCode = addr.getCountryCode();
					if (!TextUtils.isEmpty(strCntyCode))
						return strCntyCode.toLowerCase();
	            }
			} catch (Exception e) {
				if (aDebug.ENABLED)
					aDebug.d(LOG_TAG, "_GetCountryCodeFromLocation()", e);
			}
		}
		
		return null;
	}
	
	private static String
	_GetCountryCodeFromSimCard(Context ctx)
	{
		String strCntyISO;
		
		TelephonyManager tm = (TelephonyManager)ctx.getSystemService(Context.TELEPHONY_SERVICE);
		if (aDebug.ENABLED) {
			//us, US, tw
			aDebug.d(LOG_TAG, "SimCountryIso=" + tm.getSimCountryIso());
			aDebug.d(LOG_TAG, "NetworkCountryIso=" + tm.getNetworkCountryIso());
		}
		
		strCntyISO = tm.getSimCountryIso();
		if (TextUtils.isEmpty(strCntyISO))
			strCntyISO = tm.getNetworkCountryIso();
		
		return strCntyISO.toLowerCase();
	}
	
	static String
	GetUserID()
	{
		Context ctx = MiscUtil.GetAppContext();
		
		String str;
		
		str = _GetUserIDStackOverflowVersion(ctx);
		if (!TextUtils.isEmpty(str)) return str;
		
    	return UserIDGoogleVersion.GetID(ctx);
	}
	
	//http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id
	//need android.permission.READ_PHONE_STATE permission
	//changed when device wipe
	private static String
	_GetUserIDStackOverflowVersion(Context ctx)
	{
	    final String strSerial, androidId;
        //TelephonyManager tm = (TelephonyManager)ctx.getSystemService(Context.TELEPHONY_SERVICE);
	    //tmDevice = "" + tm.getDeviceId(); //need READ_PHONE_STATE
	    //tmSerial = "" + tm.getSimSerialNumber(); //need READ_PHONE_STATE
	    androidId = "" + android.provider.Settings.Secure.getString(ctx.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
	    strSerial = "" + aSystemProperties.Get("ro.serialno");
	    if (aDebug.ENABLED) {
	        aDebug.d(LOG_TAG, "_GetUserIDStackOverflowVersion");
            aDebug.d(LOG_TAG, "  strSerial=" + strSerial);
            aDebug.d(LOG_TAG, "  androidId=" + androidId);
        }
	    
	    if (TextUtils.isEmpty(strSerial) && TextUtils.isEmpty(androidId))
	    	return null;

        final String str1 = TextUtils.isEmpty(androidId) ? strSerial : androidId;
	    final String str2 = TextUtils.isEmpty(strSerial) ? androidId : strSerial;
	    
	    //UUID deviceUuid = new UUID(androidId.hashCode(), ((long)tmDevice.hashCode() << 32) | tmSerial.hashCode());
        UUID deviceUuid = new UUID(((long)str2.hashCode() << 30) ^ ((long)str1.hashCode()), 
                                   ((long)str1.hashCode() << 28) ^ ((long)str2.hashCode()));

        //reverse UUID
        String strUUID = new StringBuilder(deviceUuid.toString()).reverse().toString();
	    
        if (aDebug.ENABLED) {
			aDebug.d(LOG_TAG, "  user_id=" + strUUID);
		}
	    return strUUID;
	}
	
	//http://android-developers.blogspot.com/2011/03/identifying-app-installations.html
	//changed when uninstall
	private static class UserIDGoogleVersion {
	    private static String sID = null;
	    private static final String INSTALLATION = "INSTALLATION";

	    public synchronized static String 
	    GetID(Context context) 
	    {
	        if (sID == null) {  
	            File installation = new File(context.getFilesDir(), INSTALLATION);
	            try {
	                if (!installation.exists())
	                    _WriteInstallationFile(installation);
	                sID = _ReadInstallationFile(installation);
	            } catch (Exception e) {
	                throw new RuntimeException(e);
	            }
	        }
	        
	        if (aDebug.ENABLED) {
				aDebug.d(LOG_TAG, "UserIDGoogleVersion=" + sID);
			}
	        return sID;
	    }

	    private static String 
	    _ReadInstallationFile(File installation) throws IOException 
	    {
	        RandomAccessFile f = new RandomAccessFile(installation, "r");
	        byte[] bytes = new byte[(int) f.length()];
	        f.readFully(bytes);
	        f.close();
	        return new String(bytes);
	    }

	    private static void 
	    _WriteInstallationFile(File installation) throws IOException 
	    {
	        FileOutputStream out = new FileOutputStream(installation);
            //reverse UUID
	        String id = new StringBuilder(UUID.randomUUID().toString()).reverse().toString();
	        out.write(id.getBytes());
	        out.close();
	    }
	}
}
